#include "StringRef.h"
#include <math.h>

const StringRef NULL_STRING((const rchar *)0);

void StringRef::Buffer::construct(const rchar *str, size_t len)
{
	length = len;
	rchar *b = new rchar[length+1];
	memcpy(b, str, length*sizeof(rchar));
	b[length] = 0;
	
	buffer = b;
}

void StringRef::copyFrom(const StringRef &s)
{
	switch(s.type)
	{
	case NULLREF:
	case LITERAL:
		// literal pointers can be and are shared
		type = s.type;
		data = s.data;
		break;
	case STD_STRING:
		// std::wstrings are copied, because they are considered to be short-lifetime pointers
		type = ALLOCATED;
		buffer = new Buffer(s.data);
		break;
	case ALLOCATED:
		type = ALLOCATED;
		buffer = s.buffer;
		buffer->addRef();
		break;
	}
}

void StringRef::release()
{
	if(type == ALLOCATED)
		buffer->release();
}

int StringRef::toInt(int defaultValue) const
{
	if(type == NULLREF)
		return defaultValue;
	int result = 0;
	int l = getLength();
	const rchar *buf = getBuffer();
	for(int i=0; i<l; i++)
	{
		if(buf[i] < '0' || buf[i] > '9')
			return defaultValue;
		result = result*10+buf[i]-'0';
	}
	return result;
}

float StringRef::toFloat(float defaultValue) const
{
	if(type == NULLREF)
		return defaultValue;
	const float ONE_TENTH = float(1)/float(10);

	float result = 0;
	float resultFrac = ONE_TENTH;
	enum { MODE_INT, MODE_FRAC, MODE_EXP } mode = MODE_INT;
	int l = getLength();
	const rchar *buf = getBuffer();
	bool neg = false, expneg = false;
	int expVal = 0;
	if(buf[0] == '+' || buf[0] == '-')
	{
		neg = buf[0] == '-';
		buf++;
		l--;
	}
	for(int i=0; i<l; i++)
	{
		if((buf[i] < L'0' || buf[i] > L'9') && (mode != MODE_INT || buf[i] != L'.')
			&& (mode != MODE_FRAC || buf[i] != L'e' && buf[i] != L'E'))
			return defaultValue;
		if(mode == MODE_FRAC)
		{
			if(buf[i] == 'e' || buf[i] == 'E')
			{
				mode = MODE_EXP;
				if(buf[i+1] == '+' || buf[i+1] == '-')
					expneg = buf[++i] == '-';
			} else
			{
				result += resultFrac*(buf[i]-L'0');
				resultFrac *= ONE_TENTH;
			}
		} else
		if(mode == MODE_INT)
		{
			if(buf[i] == '.')
			{
				mode = MODE_FRAC;
			} else
			{
				result = result*10+buf[i]-L'0';
			}
		} else
		if(mode == MODE_EXP)
		{
			expVal = expVal*10+(buf[i]-L'0');
		}
	}

	if(expVal)
	{
		if(expneg)
			expVal = -expVal;
		result = result * pow(float(10), expVal);
	}

	return neg ? -result : result;
}


void utf8cpy(wchar_t *dest, int dstLen, const char *utf8)
{
	unsigned char *p = (unsigned char*)utf8;
	int dp = 0;
	while(*p && dp < dstLen-1)
	{
		if(*p&0x80)
		{
			if((p[0]&0xE0) == 0xC0)
			{
				dest[dp++] = ((p[0]&0x1F)<<6) | (p[1]&0x3F);
				p++;
			}
			if((p[0]&0xF0) == 0xE0)
			{
				dest[dp++] = ((p[0]&0x0F)<<12) | ((p[1]&0x3F)<<6) | (p[2]&0x3F);
				p += 2;
			}
		} else
		{
			dest[dp++] = *p;
		}
		p++;
	}
	dest[dp++] = 0;
}

#define         MASKBITS                0x3F
#define         MASKBYTE                0x80
#define         MASK2BYTES              0xC0
#define         MASK3BYTES              0xE0
#define         MASK4BYTES              0xF0
#define         MASK5BYTES              0xF8
#define         MASK6BYTES              0xFC

rchar* utf8dup(const wchar_t *input)
{
	RCharBuffer output;
	for(const wchar_t *c = input; *c; ++c)
	{
		// 0xxxxxxx
		if(*c < 0x80)
		{
			output.pushBack((rchar)*c);
		} else
		// 110xxxxx 10xxxxxx
		if(*c < 0x800)
		{
			output.pushBack((rchar)(MASK2BYTES | *c >> 6));
			output.pushBack((rchar)(MASKBYTE | *c & MASKBITS));
		} else
		// 1110xxxx 10xxxxxx 10xxxxxx
		if(*c < 0x10000)
		{
			output.pushBack((rchar)(MASK3BYTES | *c >> 12));
			output.pushBack((rchar)(MASKBYTE | *c >> 6 & MASKBITS));
			output.pushBack((rchar)(MASKBYTE | *c & MASKBITS));
		}
	}

	output.pushBack(0);

	return output.getBufferAndReset();
}
